#include "stdafx.h"
#include "pipe_module_impl.h"
#include <vector>
#include "xml.h"
#include "strutil.h"
#include "pipe_impl.h"
#include "pipe_event_handler.h"
#include "pipe_protocol.h"
#include <sstream>

using namespace std;
using namespace utility;
using namespace utility::xml;

namespace utility
{
namespace network
{
pipe_module_impl * pipe_module_impl::instance_ = NULL;

pipe_module_impl * pipe_module_impl::instance()
{
    if (instance_ == NULL)
        instance_ = new pipe_module_impl;
    return instance_;
}

void pipe_module_impl::destroy_instance()
{
    delete instance_;
    instance_ = NULL;
}

pipe_module_impl::pipe_module_impl() :
    net_module_(NULL)
{
    pipe_id_.number = 0;
}

pipe_module_impl::~pipe_module_impl()
{

}

bool pipe_module_impl::initialize(const char *configure_file, pipe_manager *pipe_mgr, net_module * netmodule)
{
    if (netmodule == NULL)
    {
        last_error_ = "pipe_module_impl::initialize failed: net module is NULL";
        return false;
    }

    if (pipe_mgr == NULL)
    {
        last_error_ = "pipe_module_impl::initialize failed: pipe_mgr is NULL";
        return false;
    }

    pipe_manager_ = pipe_mgr;

    net_module_ = netmodule;
    if (!_load_file(configure_file))
    {
        last_error_ += "\npipe_module_impl::initialize failed: pipe_mgr is NULL";
        return false;
    }

    pipe_info::ports_itr i;
    for (i = pipe_info_.listen_ports_.begin(); i != pipe_info_.listen_ports_.end(); i++)
    {
        if (!add_listener((*i).c_str()))
        {
            ostringstream oss;
            oss << "cannot listen to the port:" << (*i).c_str();
            last_error_ = oss.str();
            return false;
        }
    }

    pipe_info::connection_itr j;
    for (j = pipe_info_.connection_info_.begin(); j != pipe_info_.connection_info_.end(); j++)
    {
        add_connection((*j).remote_ip_.c_str(), 
            (*j).remote_port_.c_str(), 
            atoi((*j).interval_.c_str()));
    }
    
    return true;
}

PIPEID pipe_module_impl::get_local_id()
{
    return pipe_id_;
}

bool pipe_module_impl::add_listener(const char * port)
{
    if (net_module_)
    {
        listener * l = net_module_->create_listener();
        l->set_session_mgr(this);
        return l->start("0.0.0.0", port);
    }
    return false;
}

void pipe_module_impl::add_connection(const char * remote_ip, const char * port, uint32 reconnect_interval)
{
    if (net_module_)
    {
        connector * c = net_module_->create_connector();
        pipe_connector_event_handler * event_handler = connector_event_pool_.construct(reconnect_interval);
        c->set_event_handler(event_handler);
        event_handler->set_connector(c);
        c->connect(remote_ip, port);
    }
}

bool pipe_module_impl::_load_file(const char * file)
{
    CMarkupSTL xml_doc;
    if (!xml_doc.Load(file))
    {
        last_error_ = xml_doc.GetError();
        return false;
    }

    if (!xml_doc.FindElem("config")) 
    {
        last_error_ = "Cannot find element: config.";
        return false;
    }
    xml_doc.IntoElem();

    if (!xml_doc.FindElem("connections"))
    {
        last_error_ = "Cannot find element: config\\connections";
        return false;
    }

    string id = xml_doc.GetAttrib("local_id");
    if (id.empty())
    {
        last_error_ = "Cannot find attribute: config\\connections.local_id";
        return false;
    }
    pipe_id_ = atoid(id.c_str());
    if (pipe_id_.number == 0)
    {
        last_error_ = "Cannot parse attribute: config\\connections.local_id";
        return false;
    }
    xml_doc.IntoElem();

    while(xml_doc.FindElem("connection"))
    {
        string mode = xml_doc.GetAttrib("mode");
        if (mode == "listen")
        {
            string port = xml_doc.GetAttrib("port");
            if (port.empty())
            {
                last_error_ = "Cannot parse attribute: config\\connections\\connection.port";
                return false;
            }
            pipe_info_.add_listener(port);
        }
        else if (mode == "connect")
        {
            xml_doc.IntoElem();
            while (xml_doc.FindElem("item"))
            {
                string id = xml_doc.GetAttrib("id");
                if (id.empty())
                {
                    last_error_ = "Cannot parse attribute: config\\connections\\connection\\item.id";
                    return false;
                }

                string ip = xml_doc.GetAttrib("remote_ip");
                if (ip.empty())
                {
                    last_error_ = "Cannot parse attribute: config\\connections\\connection\\item.remote_ip";
                    return false;
                }

                string port = xml_doc.GetAttrib("port");
                if (port.empty())
                {
                    last_error_ = "Cannot parse attribute: config\\connections\\connection\\item.port";
                    return false;
                }

                string interval = xml_doc.GetAttrib("reconnect_interval");
                if (interval.empty())
                {
                    interval = "5";
                }

                pipe_info_.add_connection(id.c_str(), ip.c_str(), port.c_str(), interval.c_str());
            }
            xml_doc.OutOfElem();
        }

    }

    return true;
}

const char * pipe_module_impl::last_error()
{
    return last_error_.c_str();
}

void pipe_module_impl::on_accept(session * s)
{
    pipe_listener_event_handler * handler = listener_event_pool_.construct();
    s->set_event_handler(handler);
    handler->set_session(s);
}

void pipe_module_impl::send_local_id(session * s)
{
    pipe_id_ntf message;
    message.header.op_type = 1;
    message.id = pipe_id_;
    s->send(&message, sizeof(message));
}

pipe_manager * pipe_module_impl::get_pipe_manager()
{
    return pipe_manager_;
}

void pipe_module_impl:: recycle_listener_handler(pipe_listener_event_handler * handler)
{
    listener_event_pool_.destroy(handler);
}

void pipe_module_impl::destroy_connection( pipe_connector_event_handler * handler )
{
    handler->get_connector()->destroy();
    connector_event_pool_.destroy(handler);
}

PIPEID atoid(const char * str)
{
    PIPEID id;
    id.number = 0;
    string id_str(str);
    vector<string> result;
    strutil::SplitStringUsing(id_str, "-", &result);
    if (result.size() < 4)
        return id;
    
    id.detail_info.region = atoi(result[0].c_str());
    id.detail_info.group  = atoi(result[1].c_str());
    id.detail_info.type   = atoi(result[2].c_str());
    id.detail_info.index  = atoi(result[3].c_str());
    return id;
}

std::string idtoa(PIPEID pipe_id)
{
    char result[16] = {0};
    sprintf(result, "%d-%d-%d-%d", 
        pipe_id.detail_info.region, 
        pipe_id.detail_info.group,
        pipe_id.detail_info.type,
        pipe_id.detail_info.index
        );
    return string(result);
}

}   // end of namespace network
}   // end of namespace utility

utility::network::pipe_module * get_pipe_module()
{
    return utility::network::pipe_module_impl::instance();
}

void destroy_pipe_module()
{
    utility::network::pipe_module_impl::destroy_instance();
}

